﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Console.xaml.cs" company="Sitecore A/S">
//   Copyright (C) by Sitecore A/S
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Threading;
using Newtonsoft.Json;

namespace Sitecore.VisualStudio.UI.PowerShellConsoles
{
    using System.ServiceModel;
    using System.Windows;
    using AurelienRibon.Ui.Terminal;
    using Sitecore.VisualStudio.Annotations;
    using Sitecore.VisualStudio.Data;
    using Sitecore.Rocks.PowerShell.PowerShellConsole;

    /// <summary>
    /// Interaction logic for Console.xaml
    /// </summary>
    public partial class Console
    {
        #region Constructors and Destructors

        /// <summary>Initializes a new instance of the <see cref="Console"/> class.</summary>
        public Console()
        {
            this.InitializeComponent();

            this.Loaded += this.ControlLoaded;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the database URI.
        /// </summary>
        /// <value>The database URI.</value>
        [NotNull]
        public DatabaseUri DatabaseUri { get; set; }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the client.
        /// </summary>
        /// <value>The client.</value>
        [NotNull]
        protected PowerShellWebServiceSoapClient Client { get; private set; }

        //protected PowerShellWebService Client { get; private set; }

        /// <summary>
        /// Gets or sets the GUID.
        /// </summary>
        /// <value>The GUID.</value>
        [NotNull]
        protected string Guid { get; private set; }

        public string StartItemPath { get; set; }

        #endregion

        #region Methods

        /// <summary>Controls the loaded.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
        private void ControlLoaded(object sender, RoutedEventArgs e)
        {
            this.Loaded -= this.ControlLoaded;

            var address = this.DatabaseUri.Site.Connection.HostName;
            if (address.IndexOf(@"://") < 0)
            {
                address = @"http://" + address;
            }

            address += "/Console/Services/PowerShellWebService.asmx";

            var endpoint = new EndpointAddress(address);
            var binding = new BasicHttpContextBinding
                              {
                                  HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
                                  MaxBufferSize = 512*1024,
                                  MaxReceivedMessageSize = 512*1024,
                                  
                              };
            binding.ReaderQuotas.MaxStringContentLength = 128*1024;
            Client = new PowerShellWebServiceSoapClient(binding, endpoint);
            Guid = System.Guid.NewGuid().ToString("B").ToUpperInvariant();

            Terminal.CommandEntered += this.Execute;
            Terminal.Text += "Sitecore PowerShell Console\n";
            Terminal.Text += "Copyright (c) 2010-2012 Cognifide Limited & Adam Najmanowicz.\n";
            Terminal.Text += "All rights Reserved\n\n";
            Terminal.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            Terminal.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
            Terminal.AutoCompleteRequested += new EventHandler<AurelienRibon.Ui.Terminal.Terminal.CommandEventArgs>(Terminal_AutoCompleteRequested);
            try
            {

                Result result = ExecutePowerShellCommand(string.Format("cd \"{0}\"", StartItemPath));

                while (result.status != "complete" && result.status != "error")
                {
                    Thread.Sleep(100);
                    result = PollCommandResults(result);
                }
                Terminal.Prompt = result.prompt;
            }
            catch (EndpointNotFoundException ex)
            {
                Terminal.Text += "PowerShell Console was not found on the server.\n\n";
                Terminal.Text += "Please Visit Sitecore Marketplace:\n";
                Terminal.Text += "http://marketplace.sitecore.net/Modules/Sitecore_PowerShell_console.aspx\n";
                Terminal.Text += "Download, install the console on your server and try again.\n\n\n";

            }
        }

        void Terminal_AutoCompleteRequested(object sender, Terminal.CommandEventArgs e)
        {
            AnimationVisibility(Visibility.Visible);

            ArrayOfString result =
                Client.CompleteRocksCommand(Guid, e.Command.Raw,
                                                 DatabaseUri.Site.Connection.UserName,
                                                 DatabaseUri.Site.Connection.Password);
            e.Command.Args = result.ToArray();
            //return JsonConvert.DeserializeObject<Result>(result);

            AnimationVisibility(Visibility.Hidden);
        }

        /// <summary>Executes the specified sender.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="AurelienRibon.Ui.Terminal.Terminal.CommandEventArgs"/> instance containing the event data.</param>
        private void Execute(object sender, Terminal.CommandEventArgs e)
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

            worker.DoWork += new DoWorkEventHandler((o, args) =>
                                                        {
                                                            this.Terminal.Dispatcher.BeginInvoke(
                                                                DispatcherPriority.Normal,
                                                                new Action<Visibility>(AnimationVisibility),
                                                                Visibility.Visible);
                                                            Result result = ExecutePowerShellCommand(e.Command.Raw);
                                                            this.Terminal.Dispatcher.BeginInvoke(
                                                                DispatcherPriority.Normal,
                                                                new Action<string>(SetResponse),
                                                                result.result);

                                                            while (result.status != "complete" && result.status != "error")
                                                            {
                                                                Thread.Sleep(50);
                                                                result = PollCommandResults(result);
                                                                //this.Terminal.Text += result.result;
                                                                this.Terminal.Dispatcher.BeginInvoke(
                                                                    DispatcherPriority.Normal,
                                                                    new Action<string>(SetResponse),
                                                                    result.result);
                                                            }
                                                            this.Terminal.Dispatcher.BeginInvoke(
                                                                DispatcherPriority.Normal,
                                                                new Action<string>(SetPrompt),
                                                                result.prompt);
                                                            this.Terminal.Dispatcher.BeginInvoke(
                                                                DispatcherPriority.Normal,
                                                                new Action<Visibility>(AnimationVisibility),
                                                                Visibility.Hidden);
                                                        });
            worker.RunWorkerAsync();
        }

        private void SetResponse(string result)
        {
            this.Terminal.Text += result;
        }

        private void SetPrompt(string prompt)
        {
            this.Terminal.Prompt = prompt;
            this.Terminal.InsertNewPrompt();
        }

        private void AnimationVisibility(Visibility visibility)
        {
            LoadingAnimation.Visibility = visibility;
        }
        
        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if(e.Error != null)
            {
                this.Terminal.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new Action<string>(SetResponse),
                    "The following error has appeared:\n"+e.Error.Message);
                
            }
        }

        private Result ExecutePowerShellCommand(string command)
        {
            string result = this.Client.ExecuteRocksCommand(this.Guid, command,
                                                            this.DatabaseUri.Site.Connection.UserName,
                                                            this.DatabaseUri.Site.Connection.Password).ToString();
            return JsonConvert.DeserializeObject<Result>(result);
        }

        private Result PollCommandResults(Result result)
        {
            string serviceResult =
                Client.PollCommandOutput(this.Guid,
                                         result.handle,
                                         "text").ToString();
            result = JsonConvert.DeserializeObject<Result>(serviceResult);
            return result;
        }

        #endregion
    }

    public class Result
    {
        public string status;
        public string handle;
        public string result;
        public string prompt;
    }

}